package controller;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JOptionPane;

import view.MainFrame;
import model.*;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Date;

import model.ACDirector;
import model.Address;
import model.Auction;
import model.AuctionCentralEmployee;
import model.Bidder;
import model.CreditCard;
import model.Item;
import model.NonProfit;
import model.TimeObject;
import model.User;

/**
 * @author abdielcabrera
 * @author kevin Smith
 * @author Bob Landowski
 * @author Eric Struck
 * 
 */
public class ACSystem implements Observer {

	/** This list will hold all active addresses */
	private static LinkedList<Address> my_addresses = new LinkedList<Address>();

	/** This list will hold all active auctions */
	private static LinkedList<Auction> my_auctions = new LinkedList<Auction>();

	/** This list will hold all expired auctions */
	private static LinkedList<Auction> my_expired_auctions = new LinkedList<Auction>();

	/** This list will hold all items */
	//private static LinkedList<Item> my_items = new LinkedList<Item>();

	/** This list will hold all active credit cards */
	private static LinkedList<CreditCard> my_creditCards = new LinkedList<CreditCard>();

	/**Keep track of how many user objects have been created. */
	private static int userCount = 0;

	/**Keep track of how many auction objects have been created. */
	//private static int auctionCount = 0;

	/** This list will hold all active users */
	private static LinkedList<User> my_users = new LinkedList<User>();

	/** Username of the current user */
	private String my_userName;

	/** Password of the active user */
	private String my_passWord;

	/** reference to the mainframe owned by this class */
	public static MainFrame my_mainframe;

	/** The current user logged in. */
	private User my_current_user;

	public User getCurrentUser() {
		return my_current_user;
	}

	/** The AuctionCalendar Instance */
	private static AuctionCalendar my_AuctionCalendar = new AuctionCalendar();
	public int calendarBandaid =1;

	/**
	 * The current state (mode of operation) the GUI is currently in.
	 * 
	 * @see StateOfGUI
	 */
	private WestPanelEnum my_westPanel;
	private UserTypeEnum my_eastPanel;

	/**
	 * Constructor for the Controller. State initially set to LOGIN
	 * 
	 * @throws Exception
	 */
	public ACSystem() {
		Auction.reset(); //sets static variables back to 0
		Item.reset();
	}

	/**
	 * Sets the userName of the User
	 * 
	 * @pre the_userName != null
	 * @param my_userName
	 *            the my_userName to set
	 */
	public void setMy_userName(String the_userName) {
		my_userName = the_userName;
		// System.out.println(my_userName);
	}

	public void setMy_password(String the_passWord) {
		my_passWord = the_passWord;
		// System.out.println(my_passWord);
	} 

	/**
	 * 
	 * @return
	 */
	public WestPanelEnum getWestPanel() {
		return my_westPanel;
	}

	/**
	 * 
	 * @param the_westPanel
	 */
	public void setWestPanel(WestPanelEnum the_westPanel) {

		my_westPanel = the_westPanel;

	}

	/**
	 * 
	 * @return
	 */
	public UserTypeEnum getEastPanel() {
		return my_eastPanel;
	}

	/**
	 * 
	 * @param the_eastPanel
	 */
	public void setEastPanel(UserTypeEnum the_eastPanel) {

		my_eastPanel = the_eastPanel;
	}

	/**
	 * Checks the username and password and sets current user if valid sets to
	 * null if invalid
	 */
	public void checkCredentials() {
		boolean found = false;
		for(int i = 0; i < my_users.size(); i++) {
			if (my_users.get(i).getMy_userName().equals(my_userName) && 
				my_users.get(i).getMy_password().equals(my_passWord)) {
				my_current_user = my_users.get(i);
				setEastPanel(my_current_user.get_userType());
				setWestPanel(WestPanelEnum.CALENDAR);
				my_mainframe.westPanelState(my_current_user);
				my_mainframe.eastPanelState();
				if(my_current_user.getMy_accessLevel() == 3) {
					if (((NonProfit)my_current_user).getCurrentAuction() != null) {
						my_mainframe.my_EastPanelNP.disableMe();
					}
				}
				found = true;
				break;
			} 
		}
		if (!found) {
			JOptionPane.showMessageDialog(my_mainframe, "Invalid entry, Please try again",
					"Invalid User or password error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	public MainFrame getMainFrame() {
		return my_mainframe;

	}

	public AuctionCalendar getAuctionCalendar() {
		return my_AuctionCalendar;
	}

	public void addUser(final User u) {
		boolean taken = false;
		for(int i = 0; i < my_users.size(); i++) {
			if (my_users.get(i).getMy_userName() == u.getMy_userName()) {
				taken = true;
				break;
			}
		}
		if (!taken)	my_users.add(u);
	}

	/** DO NOT USE!  Use AuctionCalendarName.addAuction!! */
	public static void addAuction(final Auction the_auction) {
		my_auctions.add(the_auction);
	}

	public LinkedList<Auction> getAuctionList() {
		return my_auctions;
	}

	public void sortAuctionList() {
		boolean swapped;
		int size = my_auctions.size();
		Date datePrevious = null;
		Date dateCurrent = null;
		Auction auctionPrevious = null;
		Auction auctionCurrent = null;
		Auction auctionTemp = null;

		// bubble sort
		do {   
			swapped = false;
			for (int i = 1; i < size; i++) { // or == size - 1
				/* if this pair is out of order */
				// getAuctionDate().compareTo(anotherDate) Returns:
				// the value 0 if the argument Date is equal to this Date; 
				// a value less than 0 if this Date is before the Date argument; 
				// and a value greater than 0 if this Date is after the Date argument.
				auctionPrevious = my_auctions.get(i-1);
				auctionCurrent = my_auctions.get(i);
				datePrevious = auctionPrevious.getAuctionDate();
				dateCurrent = auctionCurrent.getAuctionDate();

				if (datePrevious.compareTo(dateCurrent) > 0) {
					/* swap the auctions and remember something changed */
					auctionTemp = auctionPrevious;
					auctionPrevious = auctionCurrent;
					auctionCurrent = auctionTemp;
					my_auctions.set(i-1, auctionPrevious);
					my_auctions.set(i, auctionCurrent);
					swapped = true;
				} // end if
			} //end for
		} while (!swapped);
	}

	@SuppressWarnings("deprecation")
	public void removeExpiredAuction() {
		Date currentDate = new Date();
		currentDate.setDate(12); // day
		currentDate.setMonth(2); // March
		currentDate.setYear(2014 - 1900); // year = 2014
		int size = my_auctions.size();
		Auction auctionTemp = null;
		Date dateTemp = new Date();

		for (int i = 1; i < size; i++) {
			auctionTemp = my_auctions.get(i);
			dateTemp = my_auctions.get(i).getAuctionDate();
			// getAuctionDate().compareTo(anotherDate) Returns:
			// a value less than 0 if this Date is before the Date argument:
			if (dateTemp.compareTo(currentDate) < 0) {
				// remove expired from my_auctions
				my_auctions.remove(i);
				size--; // have to decrease the size because one element was removed
				// add expired to my_expired auctions:
				my_expired_auctions.add(auctionTemp);
			}
		}
	}


	/**
	 * Main.
	 * 
	 * @param args
	 */
	public static void main(String[] args) throws IOException {
		
		final ACSystem my_controller = new ACSystem();
		// Call methods to create the objects from database:
		readFromDatabase();

		// GUI:
		
		my_mainframe = new MainFrame(my_controller); // Instantiates the MainFrame.
		
		
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
	        public void run() {
	        	my_controller.write();
	        }
	    }, "Shutdown-thread"));
	}

	/**
	 * Return an specific User by its last name.
	 * 
	 *  @param the_last_name The last name of the user
	 *  @return the User found or null if not foulnd.
	 */
	public User findUser (String the_last_name) {
		User answer = null;
		User tempUser = null;
		for (int i = 0; i < my_users.size(); i++) {
			tempUser = my_users.get(i);
			if (tempUser.get_last_name().equalsIgnoreCase(the_last_name)) {
				answer = tempUser;
				break;
			}
		}
		return answer;
	}

	/* **********      READ FROM FILE     ************* */

	/**
	 * Create the objects from database.
	 * 
	 * @throws IOException
	 */
	private static void readFromDatabase() throws IOException {
		createAddressesObjects();                      // **** TESTED
		createCreditCardObjects();                     // **** TESTED
		createUserObjects("datafiles/nonProfit.txt");  // **** TESTED
		createAuctionObjects();                        // **** TESTED
		createItemObjects();                           // **** TESTED
		createUserObjects("datafiles/Bidder.txt");     // **** TESTED
		createUserObjects("datafiles/AuctionCentralEmployee.txt");  // **** TESTED
		createUserObjects("datafiles/ACDirector.txt"); // **** TESTED
		// System.out.println(my_creditCards);         // **** TESTED
		// System.out.println(my_addresses);           // **** TESTED
		// System.out.println(my_users);               // **** TESTED
		// System.out.println(my_users.get(0).getMy_address());  // **** TESTED 
		// System.out.println(my_auctions);            // **** TESTED
		// System.out.println(my_items);               // **** TESTED
	}

	/**
	 * Loads the Address Objects from database. 
	 */	
	private static void createAddressesObjects() throws IOException{
		BufferedReader br = new BufferedReader(new FileReader("datafiles/Address.txt"));
		String one_line;
		String[] word;
		// if there is a line to read then:
		while ((one_line = br.readLine()) != null){ 
			word = one_line.split("~");
			my_addresses.add(new Address(word[0], word[1], word[2], word[3], word[4], word[5]));
			br.ready();                
		}// end of while loop
		br.close();
	}

	/**
	 * Loads the Credit Cards Objects from database. 
	 */	
	private static void createCreditCardObjects() throws IOException{
		// String the_number, int the_exp_year, int the_exp_month, String the_signature_number
		BufferedReader br = new BufferedReader(new FileReader("datafiles/CreditCard.txt"));
		String one_line;
		String[] word;
		// if there is a line to read then:
		while ((one_line = br.readLine()) != null){ 
			word = one_line.split("~");
			if (isNumeric(word[1]) && isNumeric(word[2])) {
				my_creditCards.add(new CreditCard(word[0], (int) Double.parseDouble(word[1]), (int) Double.parseDouble(word[2]), word[3]));
			}
			br.ready();                
		}// end of while loop
		br.close();
	}

	/**
	 * Loads the User Objects from database. 
	 */	
	private static void createUserObjects(String the_file) throws IOException{
		BufferedReader br = new BufferedReader(new FileReader(the_file));
		String one_line;
		String[] word;
		Bidder tempBidder = null;
		int item_number = 0;
		Item item = null;
		double amount = 0;
		// if there is a line to read then:
		while ((one_line = br.readLine()) != null){ 
			word = one_line.split("~");
			if (the_file.equals("datafiles/nonProfit.txt")) {
				// create nonProfits
				my_users.add(new NonProfit(word[0], word[1], word[2], word[3], word[4], my_addresses.get(userCount), word[6], word[7]));
				userCount++;
			} else if (the_file.equals("datafiles/Bidder.txt")) {
				// create bidders
				if (!word[0].equals("b")) { // if the first word is not a "b" then its a Bidder object
					tempBidder = new Bidder(word[0], word[1], word[2], word[3], word[4], my_addresses.get(userCount), word[6]);
					my_users.add(tempBidder); // bidder created
					userCount++;
				} else if (word[0].equals("b")) {
					// else if the first word is a "b" then is a bid that corresponds to the tempBidder
					if (isNumeric(word[1])) item_number = (int) Double.parseDouble(word[1]);
					// now checking if this item number correspond to an item on my list:
					boolean found = false;
					for (int i = 0; i < my_auctions.size(); i++){
						for(int j = 0; j < my_auctions.get(i).getItemList().size(); j++) {
							item = my_auctions.get(i).getItemList().get(j);
							if (item.getMyItemNumber() == item_number) {
								// item found:
								found = true;
								break;
							}
						}
						if (found) break;
					} // end of for-loop
					if (isNumeric(word[2])) amount = Double.parseDouble(word[2]);
					// Now we have the item, the bidder and the amount
					// Thus, place bid.
					if (item != null) { // check for null to make sure
						Bid my_temp_bid = new Bid (item,tempBidder, amount);
						tempBidder.placeBid(my_temp_bid);
						item.addBid(my_temp_bid);
					}
					

					// Testing: 
					//  System.out.println(tempBidder); // TESTED
					//  System.out.println(item);       // TESTED
					//  System.out.println(amount);     // TESTED
				}
				// add bidder credit card
				tempBidder.addCreditCard(my_creditCards.get(0));

			} else if (the_file.equals("datafiles/AuctionCentralEmployee.txt")) {
				// create auction central Employees
				my_users.add(new AuctionCentralEmployee(word[0], word[1], word[2], word[3], word[4], my_addresses.get(userCount), word[6], ""));
				userCount++;
			} else if (the_file.equals("datafiles/ACDirector.txt")) {
				// create Ac director
				my_users.add(new ACDirector(word[0], word[1], word[2], word[3], word[4], my_addresses.get(userCount), word[6], ""));
				userCount++;
			}
			br.ready();                
		}// end of while loop

		br.close();
	}

	/**
	 * Loads the auction Objects from database. 
	 */	
	private static void createAuctionObjects() throws IOException {
		// Auction(Date the_date, TimeObject the_time, TimeObject the_end, NonProfit the_owner)
		// TimeObject(final int the_hour, final int the_minute, final int the_duration)

		// thus file should have:
		// year~month~day~hour~minute~duration~hour~minute~duration~nonprofit

		BufferedReader br = new BufferedReader(new FileReader("datafiles/Auction.txt"));
		String one_line;
		String[] word;
		int x = 0;
		// if there is a line to read then:
		while ((one_line = br.readLine()) != null){ 
			word = one_line.split("~");
			if (isNumeric(word[0]) && isNumeric(word[1]) && isNumeric(word[2]) &&
					isNumeric(word[3]) && isNumeric(word[4]) && isNumeric(word[5]) &&
					isNumeric(word[6]) && isNumeric(word[7]) && isNumeric(word[8])) 
			{
				// year - 1900, month, date)
				@SuppressWarnings("deprecation")
				Date date = new Date((int) Double.parseDouble(word[0]) - 1900, (int) Double.parseDouble(word[1]), (int) Double.parseDouble(word[2]));
				TimeObject time1 = new TimeObject((int) Double.parseDouble(word[3]), (int) Double.parseDouble(word[4]), (int) Double.parseDouble(word[5]));
				TimeObject time2 = new TimeObject((int) Double.parseDouble(word[6]), (int) Double.parseDouble(word[7]), (int) Double.parseDouble(word[8]));
				//Files are written and read in same order, so "x" refers to the correct nonprofit
				Auction auction = new Auction(date, time1, time2, (NonProfit)my_users.get(x)); 
				// System.out.println(date);   // ** TESTED
				// System.out.println(time1);  // ** TESTED
				// System.out.println(time2);  // ** TESTED
				//my_auctions.add(auction); //This is double-adding everything (maybe even triple)
				my_AuctionCalendar.addAuction(auction);
				((NonProfit)my_users.get(x++)).setCurrentAuction(auction);
			} 
			br.ready();                
		}// end of while loop
		br.close();
	}

	/**
	 * Loads the Item Objects from database. 
	 */	
	private static void createItemObjects() throws IOException {
		// Item(Auction the_auction, String the_description, String the_photo, int the_quantity)
		BufferedReader br = new BufferedReader(new FileReader("datafiles/Item.txt"));
		String one_line;
		String[] word;
		Auction auction = null;

		// if there is a line to read then:
		while ((one_line = br.readLine()) != null){ 
			word = one_line.split("~");
			// word[0] is the auction #
			if (isNumeric(word[0])) {
				for (int i = 0; i < my_auctions.size(); i++){
					auction = my_auctions.get(i);
					// go trough all the auctions of the auction list 
					// if the number on the text file matches one of those
					// auction, then that is the one:
					if (auction.getAuctionNumber() == (int) Double.parseDouble(word[0])){
						break; // break from for-loop not from while-loop
					}
				} // end of for-loop
			} // end of if

			// last check:
			if (isNumeric(word[3]) && auction != null) {
				 auction.addItem(new Item(auction, word[1], word[2], (int) Double.parseDouble(word[3])));
				// wrong way to add items my_items.add(new Item(auction, word[1], word[2], (int) Double.parseDouble(word[3])));
			}
			br.ready();
		}// end of while loop
		br.close();
	}

	/**
	 * Checks if an specific String is numeric.
	 * 
	 * @pre str != null
	 * @return TRUE if numeric. 
	 *         FALSE otherwise. 
	 */
	public static boolean isNumeric(String str)  
	{  
		try  
		{  
			@SuppressWarnings("unused")
			double d = Double.parseDouble(str);  
		}  
		catch(NumberFormatException nfe)  
		{  
			return false;  
		}  
		return true;  
	}

	/* **********      WRITE TO FILE     ************* */

	public void write() {
		try {
			writeUsers();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		try {
			writeAuctions();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Begins the process of writing users to files
	 * @throws IOException 
	 */
	private void writeUsers() throws IOException {
		File ad = new File("datafiles/Address.txt");

		if (ad.exists()) {
			ad.delete(); //start fresh
		}
		ad.createNewFile();

		PrintStream address = null;
		try {
			address = new PrintStream(ad);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		try {
			writeNonProfits(address);
		} catch (IOException e1) {
			e1.printStackTrace();
		}

		try {
			writeBidders(address);
		} catch (IOException e) {
			e.printStackTrace();
		}	

		try {
			writeEmpoyees(address);
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			writeDirector(address);
		} catch (IOException e) {
			e.printStackTrace();
		}
		address.close();
	}

	/**
	 * Writes auctions and items to files
	 * @throws IOException
	 */
	private void writeAuctions() throws IOException {
		File auc_file = new File("datafiles/Auction.txt");
		File items = new File("datafiles/Item.txt");

		if (auc_file.exists()) {
			auc_file.delete(); //start fresh
		}
		auc_file.createNewFile();

		if (items.exists()) {
			items.delete(); //start fresh
		}
		items.createNewFile();

		PrintStream ps = new PrintStream(auc_file);
		PrintStream item_ps = new PrintStream(items);

		getAuctionCalendar(); 
		for (int i = 0; i < AuctionCalendar.getList().size(); i++) {
			if (AuctionCalendar.getList().get(i).getAuction1() != null) {
				//add auction 1
				ps.println(AuctionCalendar.getList().get(i).getAuction1());
				//add auction 1's items
				for (int j = 0; j < AuctionCalendar.getList().get(i).getAuction1().getItemList().size(); j++) {
					item_ps.println(AuctionCalendar.getList().get(i).getAuction1().getItemList().get(j).toStringTwo());
				}
			}
			if (AuctionCalendar.getList().get(i).getAuction2() != null) {
				ps.println(AuctionCalendar.getList().get(i).getAuction2());
				for (int k = 0; k < AuctionCalendar.getList().get(i).getAuction2().getItemList().size(); k++) {
					item_ps.println(AuctionCalendar.getList().get(i).getAuction2().getItemList().get(k));
				}
			}
		}

		item_ps.close();
		ps.close();
	}

	/**
	 * Writes bidders to a file and writes addresses to a file
	 * @param addr printstream for address writes
	 * @throws IOException if its broke
	 */
	private void writeBidders(final PrintStream addr) throws IOException {
		File bidder_file = new File("datafiles/Bidder.txt");
		File cc_file = new File("datafiles/CreditCard.txt");

		if (bidder_file.exists()) {
			bidder_file.delete(); //start fresh
		}
		bidder_file.createNewFile();

		if (cc_file.exists()) {
			cc_file.delete();
		}
		cc_file.createNewFile();

		PrintStream ps = new PrintStream(bidder_file);
		PrintStream cc = new PrintStream(cc_file);
		for (int i = 0; i < my_users.size(); i++) {
			if (my_users.get(i).get_userType() == UserTypeEnum.UT_BIDDER) {
				Bidder temp = (Bidder)my_users.get(i); //caste to bidder
				ps.println(temp); //print the bidder info to the bidder file
				addr.println(temp.getMy_address()); //print the address to the address file
				for (int j = 0; j < temp.findBids().size(); j++) {
					ps.println("b" + "~" + temp.findBids().get(j));   //Print the bids this bidder has
				}
				if (temp.getMy_creditCard() != null) {
					cc.println(temp.getMy_creditCard());
				}
			}
		}
		ps.close();
		cc.close();
	}

	/**
	 * Write nonprofits to files
	 * @param addr printstream for address file
	 * @throws IOException if file unable to be created
	 */
	private void writeNonProfits(final PrintStream addr) throws IOException {
		File np_file = new File("datafiles/nonProfit.txt");

		if (np_file.exists()) {
			np_file.delete(); //start fresh
		}
		np_file.createNewFile();

		PrintStream ps = new PrintStream(np_file);
		for (int i = 0; i < my_users.size(); i++) {
			if (my_users.get(i).get_userType() == UserTypeEnum.UT_NONPROFIT) {
				NonProfit temp = (NonProfit)my_users.get(i);
				ps.println(temp); //write to main file
				addr.println(temp.getMy_address()); //write address
			}
		}
		ps.close();
	}

	private void writeEmpoyees(final PrintStream addr) throws IOException {
		File e_file = new File("datafiles/AuctionCentralEmployee.txt");

		if (e_file.exists()) {
			e_file.delete(); //start fresh
		}
		e_file.createNewFile();

		PrintStream ps = new PrintStream(e_file);
		for (int i = 0; i < my_users.size(); i++) {
			if (my_users.get(i).get_userType() == UserTypeEnum.UT_ACEMPLOYEE) {
				AuctionCentralEmployee temp = (AuctionCentralEmployee)my_users.get(i);
				ps.println(temp); //write to main file
				addr.println(temp.getMy_address()); //write address
			}
		}
		ps.close();

	}

	private void writeDirector(final PrintStream addr) throws IOException {
		File dir_file = new File("datafiles/ACDirector.txt");

		if (dir_file.exists()) {
			dir_file.delete(); //start fresh
		}
		dir_file.createNewFile();

		PrintStream ps = new PrintStream(dir_file);
		for (int i = 0; i < my_users.size(); i++) {
			if (my_users.get(i).get_userType() == UserTypeEnum.UT_ACDIRECTOR) {
				ACDirector temp = (ACDirector)my_users.get(i);
				ps.println(temp); //write to main file
				addr.println(temp.getMy_address()); //write address
			}
		}
		ps.close();

	}

	@Override
	public void update(Observable arg0, Object arg1) {
		
		my_mainframe.westPanelState(my_current_user);

	}

	public static LinkedList<Auction> getMy_auctions() {
		return my_auctions;
	}

}

